﻿using AppSocketModel;
using AppSocketService.Account;
using AppSocketService.Device;
using AppSocketService.HistoryLocation;
using AppSocketService.Location;
using AppSocketService.Product;
using BCGPSCommon; 
using BCGPSMongoData;
using LJD.IMS.Utility; 
using MongoDB.Driver;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;


namespace AppSocketService.Vehicle
{
    /// <summary>
    /// 车辆服务
    /// </summary>
    public class VehicleService : ServiceBase, IVehicleService
    {
        #region var
        // 账户车辆信息表前缀 
        private readonly MongoDBCommand<VehicleDto> mongoCommand = new MongoDBCommand<VehicleDto>("AccountVehicle");
        private readonly MongoDBCommand<AccountDto> mongoDBCommandAccountPerson = new MongoDBCommand<AccountDto>("AccountPerson");
        private readonly IRedisService redisService;
        private readonly AccountDto CurrentAccount;

        private readonly ILocationService locationService; 
        private readonly IAccountService accountService;
        private readonly IDeviceService deviceService; 
        #endregion

        #region 构造函数 
        public VehicleService(IClaimsAccessor claimsAccesso, IRedisService redisService, ILocationService locationService, IAccountService accountService,IDeviceService deviceService) : base(claimsAccesso)
        {
            this.redisService = redisService;
            CurrentAccount = accountService.GetCurrentAccount();
            this.locationService = locationService; 
            this.accountService = accountService;
            this.deviceService = deviceService; 
        }

        //只在 初始化 Redis 用
        //个人账户注册时使用
        public VehicleService(IRedisService redisService)
        { 
            this.redisService = redisService;
        }
        #endregion
        
        #region  初始化REDIS 获取全部车辆列表
        public List<VehicleDto> InitRedisGetListAll()
        {
            return mongoCommand.SelectDocumentsAsync(s => s.CompanyId > 0).GetAwaiter().GetResult();
        }
        #endregion

        #region  获取公司全部车辆列表
        public List<VehicleDto> GetCompanyAll(int companyId)
        {
            List<VehicleDto> listVehicle = new List<VehicleDto>();
            var list =  redisService.GetHashAllAsync(GeneralContainer.RedisNameSpaceAccountVehicle + companyId.ToString()).GetAwaiter().GetResult();
            foreach (var item in list)
            {
                var dto = JsonConvert.DeserializeObject<VehicleDto>(item.Value, GeneralContainer.JsonSerializerSettings);//不会产生异常
                if(dto!=null)
                    listVehicle.Add(dto);
            }
            if (listVehicle.Count > 0)
                return listVehicle;
            else
                return mongoCommand.SelectDocumentsAsync(s => s.CompanyId == companyId).GetAwaiter().GetResult();
        }
        #endregion
        
        #region 添加新车辆
        public VehicleDto AddNew(VehicleRequestDto requestDto)
        {
            var vehicleDto = requestDto.As<VehicleDto>();

            var account = accountService.GetAccount(requestDto.AccountId) ?? throw new ArgumentException("账户ID错误!");

            if (!string.IsNullOrWhiteSpace(vehicleDto.DriverPhone))
                if (!StringHelper.IsMobilePhone(vehicleDto.DriverPhone))
                    throw new ArgumentException("输入的手机号不正确!");
                else
                    CheckPhone(vehicleDto.DriverPhone);

            if (vehicleDto.vehicleType == VehicleType.BUS || vehicleDto.vehicleType == VehicleType.CAR || vehicleDto.vehicleType == VehicleType.MINIBUS || vehicleDto.vehicleType == VehicleType.VAN || vehicleDto.vehicleType == VehicleType.HIGHRAILTRUCK || vehicleDto.vehicleType == VehicleType.FLATTRUCK)
            {
                if (!StringHelper.IsVehicleNo(vehicleDto.VehicleNo))
                    throw new ArgumentException("输入的车牌号不正确!");
            }

            if (string.IsNullOrWhiteSpace(vehicleDto.IMEI))
                throw new ArgumentException("IMEI必须填写!");

            if (vehicleDto.IMEI.Length < 13)
                throw new ArgumentException("请输入正确的IMEI !");

            var productService = new ProductService();
            if(!productService.IsExist(requestDto.IMEI))
                throw new ArgumentException($"IMEI[{requestDto.IMEI}]设备不在产品库,请检查输入是否正确,或向产品提供商咨询!");

            //  var dtoImei = mongoCommand.SelectDocumentAsync(s => s.CompanyId == CurrentAccount.CompanyId && s.IMEI == vehicleDto.IMEI).GetAwaiter().GetResult();
            var dtoImei = mongoCommand.SelectDocumentAsync(s => s.IMEI == vehicleDto.IMEI).GetAwaiter().GetResult();
            if (dtoImei != null)
                throw new ArgumentException($"GPS设备IMEI号[{vehicleDto.IMEI}]已经存在并被使用!");

            if (!string.IsNullOrWhiteSpace(vehicleDto.DriverPhone))
                AddAccountPerson(vehicleDto.DriverPhone);


            vehicleDto.CompanyId = CurrentAccount.CompanyId;  
            vehicleDto.CreateTime = MongoDBDateTime.GetNowUtc();
            vehicleDto.ModifyTime = MongoDBDateTime.GetNowUtc();

            mongoCommand.InsertDocumentAsync(vehicleDto).GetAwaiter().GetResult(); 

            LocationDto locationDto = new LocationDto()
            {
                CompanyId  = vehicleDto.CompanyId,
                AccountId = vehicleDto.AccountId, 
                IMEI = vehicleDto.IMEI,
                VehicleNo = vehicleDto.VehicleNo,
            };
            locationService.AddNew(locationDto);



            redisService.AddHashAsync(GeneralContainer.RedisNameSpaceAccountVehicle + CurrentAccount.CompanyId, vehicleDto.IMEI, vehicleDto).GetAwaiter().GetResult();
            redisService.AddAsync(GeneralContainer.RedisNameSpaceIMEILocation + locationDto.IMEI, locationDto);
            return vehicleDto;
        }

        private void CheckPhone(string phone)
        { 
            var dto = mongoDBCommandAccountPerson.SelectDocumentAsync(s => s.AccountCode == phone).GetAwaiter().GetResult();
            if (dto != null) 
            {
                if (dto.CompanyId != CurrentAccount.CompanyId) 
                {
                    throw new ArgumentException("手机号已经被其他用户占用,请更换后再试!");
                }
            }
        }

        private void AddAccountPerson(string phone)
        {

            var dto =  mongoDBCommandAccountPerson.SelectDocumentAsync(s => s.AccountCode == phone).GetAwaiter().GetResult();
            if (dto == null)
            {
                AccountDto accountDto = new AccountDto();
                accountDto.AccountCode = phone;
                accountDto.ParentAccountId = CurrentAccount.Id;
                accountDto.CompanyId = CurrentAccount.CompanyId;
                accountDto.Phone = phone;
                accountDto.AccountCodeType = AccountCodeType.PHONE;
                accountDto.AccountRole = AccountRole.PERSON;
                accountDto.Password = accountDto.AccountCode.SHA256(accountDto.AccountCode);
                accountDto.CreateTime = DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Utc);
                mongoDBCommandAccountPerson.InsertDocumentAsync(accountDto).GetAwaiter().GetResult();
            }
            else
            {
                if(dto.CompanyId == CurrentAccount.CompanyId)
                {
                    if (dto.ParentAccountId != CurrentAccount.Id) 
                    {
                        var update = Builders<AccountDto>.Update
                            .Set(s => s.ParentAccountId, CurrentAccount.Id);
                        mongoDBCommandAccountPerson.UpdateDocumentAsync(s => s.Id == dto.Id, update).GetAwaiter().GetResult();

                    }
                }
                else
                {
                    throw new ArgumentException("手机号已经被其他用户占用,请更换后再试!");
                }
            }
        }
        #endregion

        #region 修改车辆信息 根据 imei 
        public VehicleDto Update(VehicleDto vehicleDto)
        {
            if (!string.IsNullOrWhiteSpace(vehicleDto.DriverPhone))
                if (!StringHelper.IsMobilePhone(vehicleDto.DriverPhone))
                    throw new ArgumentException("输入的手机号不正确!");
                else
                    CheckPhone(vehicleDto.DriverPhone);

            if (vehicleDto.vehicleType == VehicleType.BUS || vehicleDto.vehicleType == VehicleType.CAR || vehicleDto.vehicleType == VehicleType.MINIBUS || vehicleDto.vehicleType == VehicleType.VAN || vehicleDto.vehicleType == VehicleType.HIGHRAILTRUCK || vehicleDto.vehicleType == VehicleType.FLATTRUCK)
            {
                if (!StringHelper.IsVehicleNo(vehicleDto.VehicleNo))
                    throw new ArgumentException("输入的车牌号不正确!");
            }

            if (string.IsNullOrWhiteSpace(vehicleDto.IMEI))
                throw new ArgumentException("IMEI必须填写!");

            if (vehicleDto.IMEI.Length < 13)
                throw new ArgumentException("请输入正确的IMEI !");


            var dto = mongoCommand.SelectDocumentAsync(s => s.CompanyId == CurrentAccount.CompanyId && s.IMEI == vehicleDto.IMEI).GetAwaiter().GetResult();
            if (dto == null)
                throw new ArgumentException("找不到车辆信息!");

 
                vehicleDto.ModifyTime = DateTime.Now;
                var update = Builders<VehicleDto>.Update
                    .Set(s => s.DriverPhone, vehicleDto.DriverPhone)
                    .Set(s => s.DriverName, vehicleDto.DriverName)
                    .Set(s => s.VehicleNo, vehicleDto.VehicleNo)
                    .Set(s => s.VehicheTypeCode, vehicleDto.VehicheTypeCode)
                    .Set(s=>s.vehicleType,vehicleDto.vehicleType)
                    .Set(s=>s.SortNo,vehicleDto.SortNo)
                    .Set(s=>s.Remark,vehicleDto.Remark)
                    .Set(s => s.ModifyTime, DateTime.Now);
                var ret = mongoCommand.UpdateDocumentAsync(s => s.CompanyId == CurrentAccount.CompanyId && s.IMEI == vehicleDto.IMEI, update).GetAwaiter().GetResult();
                if (ret == null)
                    throw new ArgumentException("修改失败,请重试!");

            dto = mongoCommand.SelectDocumentAsync(s => s.CompanyId == CurrentAccount.CompanyId && s.IMEI == vehicleDto.IMEI).GetAwaiter().GetResult();
            if (dto != null)
                redisService.AddHashAsync(GeneralContainer.RedisNameSpaceAccountVehicle + CurrentAccount.CompanyId, vehicleDto.IMEI, dto).GetAwaiter().GetResult();

            locationService.UpdateImei(dto.VehicleNo, dto.IMEI);

            if(dto.DriverPhone != vehicleDto .DriverPhone)
            {
                if(!string.IsNullOrWhiteSpace(dto.DriverPhone)) 
                    DeleteAccountPerson(dto.DriverPhone); 

                if (!string.IsNullOrWhiteSpace(vehicleDto.DriverPhone))
                    AddAccountPerson(vehicleDto.DriverPhone);
            }
            return dto;
        }

        private void DeleteAccountPerson(string phone)
        {
            var dto = mongoCommand.SelectDocumentAsync(s => s.CompanyId == CurrentAccount.CompanyId && s.DriverPhone == phone).GetAwaiter().GetResult();
            if(dto == null )
            {
                mongoDBCommandAccountPerson.DeleteDocumentAsync(s => s.CompanyId == CurrentAccount.CompanyId && s.AccountCode == phone).GetAwaiter().GetResult();
            }
        }
        #endregion

        #region 删除车辆信息
        public int Delete(string imei)
        {
            var vehicleDto = mongoCommand.SelectDocumentAsync(s => s.CompanyId == CurrentAccount.CompanyId && s.IMEI == imei).GetAwaiter().GetResult();
            
            if (vehicleDto != null)
            {
                redisService.DeleteHashObjAsync(GeneralContainer.RedisNameSpaceAccountVehicle + CurrentAccount.CompanyId, imei).GetAwaiter().GetResult();
                redisService.DeleteAsync(GeneralContainer.RedisNameSpaceIMEIAlarm + vehicleDto.IMEI).GetAwaiter().GetResult();
                int Ret = (int)mongoCommand.DeleteDocumentAsync(s => s.CompanyId == CurrentAccount.CompanyId && s.IMEI == imei).GetAwaiter().GetResult().DeletedCount;
                if (Ret > 0)
                {
                    if(!string.IsNullOrWhiteSpace( vehicleDto.DriverPhone ))
                    {
                        DeleteAccountPerson(vehicleDto.DriverPhone);
                    }

                    return locationService.Delete(vehicleDto);
                }
                else
                {
                    throw new ArgumentException("删除失败,请重试!");
                }
            }
            else
                throw new ArgumentException("删除失败,账户下没有该车!");
        }
        #endregion

        #region 获取指定账户下车辆列表 不包含子账户
        public List<VehicleDto> GetVehicleList(string accountId)
        {
            if(claimsAccessor.AccountRole ==  AccountRole .SYSTEM)
                return mongoCommand.SelectDocumentsAsync(s => s.AccountId == accountId).GetAwaiter().GetResult();
            else
                return mongoCommand.SelectDocumentsAsync(s => s.CompanyId == CurrentAccount.CompanyId && s.AccountId == accountId).GetAwaiter().GetResult();
        }
        #endregion

        #region 获取指定账户下车辆列表 不包含子账户  -- 分页
        public PageableList<VehicleDto> GetVehicleList(string accountId,int index,int size)
        {
            index = index > 0 ? index - 1 : 0; 
            if (claimsAccessor.AccountRole == AccountRole.SYSTEM)
                return mongoCommand.SelectDocumentsAsync(s => s.AccountId == accountId, s => s.VehicleNo, false, index*size, size).GetAwaiter().GetResult();
            else
                return mongoCommand.SelectDocumentsAsync(s => s.CompanyId == CurrentAccount.CompanyId && s.AccountId == accountId,s=>s.VehicleNo,false,index*size,size).GetAwaiter().GetResult();
        }
        #endregion

        #region 获取指定账户包括子账户下全部车辆
        public List<VehicleDto> GetVehicleListAll(string accountId)
        {
            var account = accountService.GetAccount(accountId);
            if (account != null && string.IsNullOrWhiteSpace(account.ParentAccountId))
            {
                return  GetCompanyAll(account.CompanyId);
            } 
            else
            {
                List<VehicleDto> list = new List<VehicleDto>();
                GetSubVehicleList(accountId, ref list);
                return list;
            }
        }
        private void GetSubVehicleList(string accountId, ref List<VehicleDto> vehicleList)
        {
            var list = GetVehicleList(accountId);
            if (list != null && list.Count > 0)
            {
                vehicleList.AddRange(list);
            }

            var accountList = accountService.GetSubAccountList(accountId);
            if (accountList == null || accountList.Count <= 0)
                return;
            foreach (var item in accountList)
            {
                GetSubVehicleList(item.AccountId, ref vehicleList);
            }
        }
        #endregion

         

        #region 通过IMEI获取车辆
        public VehicleDto GetVehicleByImei(string imei)
        {
            if (claimsAccessor.AccountRole == AccountRole.SYSTEM)
                return mongoCommand.SelectDocumentAsync(s => s.IMEI == imei).GetAwaiter().GetResult();
            else
            {
                var ret = redisService.GetHashObj<VehicleDto>(GeneralContainer.RedisNameSpaceAccountVehicle + CurrentAccount.CompanyId.ToString(), imei);
                if (ret != null)
                    return ret;
                else
                    return mongoCommand.SelectDocumentAsync(s => s.CompanyId == CurrentAccount.CompanyId && s.IMEI == imei).GetAwaiter().GetResult();
            }
        }
        #endregion


        #region 获取指定车辆信息和定位信息
        public VehicleLocationDto GetVehicleLocation(string imei,GpsMapType gpsMapType)
        {
            VehicleLocationDto dto = GetVehicleByImei(imei).As<VehicleLocationDto>();
            if(dto!=null)
            {
                dto.Location = locationService.GetLocation(dto.IMEI, gpsMapType);
                dto.Device = deviceService.Get(dto.IMEI);
            }
            return dto;
        }
        #endregion

        #region 获取指定账户下全部车辆信息和定位信息 不包括子账户
        public List<VehicleLocationDto> GetVehicleLocationList(string accountId, GpsMapType gpsMapType)
        {
            List<VehicleDto> list = GetVehicleList(accountId);

            var locationList = locationService.GetList(accountId, gpsMapType);

            List<VehicleLocationDto> vlist = list.As<List<VehicleLocationDto>>();

            foreach (var item in locationList)
            {
                var a = vlist.FirstOrDefault(s => s.IMEI == item.IMEI);
                if (a != null)
                    a.Location = item;
            }

            foreach (var item in vlist)
            {
                item.Device = deviceService.Get(item.IMEI);
            }

            return vlist;
        }
        #endregion

        #region 获取指定账户包括子账户下全部车辆信息和定位信息
        public List<VehicleLocationDto> GetVehicleLocationListAll(string accountId,GpsMapType gpsMapType)
        {
            var account = accountService.GetAccount(accountId);
            List<VehicleDto> list;
            if (account != null && string.IsNullOrWhiteSpace(account.ParentAccountId))
            {
                list = GetCompanyAll(account.CompanyId);
            }
            else
                list = GetVehicleListAll(accountId);

            var locationList = locationService.GetListAll(accountId, gpsMapType);

            List<VehicleLocationDto> vlist = list.As<List<VehicleLocationDto>>();

            foreach (var item in locationList)
            {
                var a = vlist.FirstOrDefault(s => s.IMEI == item.IMEI);
                if (a != null)
                    a.Location = item;
            }

            foreach (var item in vlist)
            {
                item.Device = deviceService.Get(item.IMEI);
            }

            return vlist;
        }
        #endregion

        #region   获取指定账户下车辆的设备信息(不包括子账户) -- 分页
        public PageableList<DeviceDto> GetDeviceList(string accountId,bool desc,int index,int size)
        {
            var ret = new PageableList<DeviceDto>();
            var retList = new List<DeviceDto>();
            var pageableList = GetVehicleList(accountId,index,size);
            if (pageableList.Items != null && pageableList.Items.Count() > 0)
            {
                ret.Items = new List<DeviceDto>();
                foreach (var item in pageableList.Items)
                {
                    var dto = deviceService.Get(item.IMEI);
                    if (dto != null)
                        retList.Add(dto);
                }
                ret.Items = retList;
                ret.PageIndex = pageableList.PageIndex;
                ret.PageSize = pageableList.PageSize;
                ret.Count = pageableList.Count;
            }
            return ret;
        }
        #endregion

        #region 设置车辆归属账户
        public VehicleDto SetVehicleAccount(string imei,string accountId)
        {
            if (string.IsNullOrWhiteSpace(accountId))
                throw new ArgumentException("请指定归属账户!");
            if(string.IsNullOrWhiteSpace(imei))
                throw new ArgumentException("请指定设备IMEI!");

            var accountDto = accountService.GetAccount(accountId);
            if (accountDto == null)
                throw new ArgumentException("账号不存在!");
            if(accountDto.CompanyId != CurrentAccount.CompanyId)
                throw new ArgumentException("账号公司信息异常!");

            var vehicleDto = mongoCommand.SelectDocumentAsync(s => s.IMEI == imei).GetAwaiter().GetResult() ?? throw new ArgumentException("指定的设备信息不存在!");

            if(vehicleDto.CompanyId != CurrentAccount.CompanyId)
                throw new ArgumentException("设备不属于本公司!");

            if (vehicleDto.AccountId == accountId)
                return vehicleDto;

            var update = Builders<VehicleDto>.Update
                .Set(s => s.AccountId, accountId) 
                .Set(s => s.ModifyTime, DateTime.Now);
            var ret = mongoCommand.UpdateDocumentAsync(s => s.CompanyId == CurrentAccount.CompanyId && s.IMEI == imei, update).GetAwaiter().GetResult() ??  throw new ArgumentException("修改失败,请重试!");

            LocationDto locationDto = locationService.SetVehicleAccount(imei,accountId) ?? throw new ArgumentException("修改失败,请重试!");

            redisService.AddHashAsync(GeneralContainer.RedisNameSpaceAccountVehicle + CurrentAccount.CompanyId, ret.IMEI, ret).GetAwaiter().GetResult();
            return ret;
        }
        #endregion
    }
}
